import asyncio
import logging
import os
import queue
import random
import re
import string
import threading

import cv2
from PIL import Image, ImageDraw, ImageFilter, ImageFont
from aiogram import types
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters import Text
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.types import InputFile, ParseMode
from dateutil.relativedelta import relativedelta
from typing import List

import client
import client_kb
import sqlite_db
from common_utils import compare_date, paste_watermark
from create_bot import dp
from handlers.users.generate.it_country.country_menu import create_italy

logger = logging.getLogger(__name__)


def process_passport_photo(user_id):
    try:
        # Load the image and face detection classifier
        img = cv2.imread(f"./files/it_dl/photo_usr/{user_id}.png", cv2.IMREAD_UNCHANGED)
        face_cascade = cv2.CascadeClassifier(
            f"./data/haarcascade_frontalface_default.xml"
        )

        # Detect faces in the image
        faces = face_cascade.detectMultiScale(img, 1.2, 5)

        # If faces are detected, crop and resize the image
        if len(faces) != 0:
            (x, y, w, h) = faces[0]
            h_top = int(h * 0.4)
            h_bottom = int(h * 0.3)
            w_left = int(w * 0.2)
            w_right = int(w * 0.2)
            x_left = max(0, x - w_left)
            x_right = min(img.shape[1], x + w + w_right)
            w_new = x_right - x_left
            y_top = max(0, y - h_top)
            y_bottom = min(img.shape[0], y + h + h_bottom)
            h_new = y_bottom - y_top

            cropped_img = img[y_top : y_top + h_new, x_left : x_left + w_new]
            cropped_img_pil = Image.fromarray(
                cv2.cvtColor(cropped_img, cv2.COLOR_BGRA2RGBA)
            )

            # Resize the image proportionally to the width of 264px
            target_width = 387
            width_ratio = target_width / cropped_img_pil.width
            new_width = target_width
            new_height = int(cropped_img_pil.height * width_ratio)
            resized_width = cropped_img_pil.resize(
                (new_width, new_height), Image.ANTIALIAS
            )

            # Resize the image proportionally to the height of 345px
            target_height = 520
            height_ratio = target_height / resized_width.height
            new_height = target_height
            new_width = int(resized_width.width * height_ratio)
            resized_height = resized_width.resize(
                (new_width, new_height), Image.ANTIALIAS
            )

            # Center crop the image to get the width of 264px
            left = (resized_height.width - target_width) / 2
            top = 0
            right = (resized_height.width + target_width) / 2
            bottom = new_height
            cropped_resized = resized_height.crop((left, top, right, bottom))

            # Convert the image to grayscale and keep the alpha channel
            grayscale_img = cropped_resized.convert("LA").convert("RGBA")
            grayscale_img.putalpha(cropped_resized.getchannel("A"))

            # Save the processed image with the alpha channel
            grayscale_img.save(f"./files/it_dl/photo_usr/{user_id}_w.png")
            os.remove(f"./files/it_dl/photo_usr/{user_id}.png")
        else:
            logger.error(f"No faces found in image for user_id {user_id}")
    except Exception as e:
        logger.error(f"Error processing passport photo for user_id {user_id}: {str(e)}")


def process_generate_signature(user_id, data):
    text = data.capitalize()
    txt = Image.new("RGBA", (1900, 600), (255, 255, 255, 0))
    go_txt = ImageDraw.Draw(txt)
    fnt1 = ImageFont.truetype(f"./fonts/1.ttf", size=270)
    go_txt.text(xy=(200, 50), text=f"{text}", font=fnt1, fill="#000000")
    fnt2 = ImageFont.truetype(f"./fonts/2.ttf", size=190)
    go_txt.text(xy=(800, 50), text=f"{text}", font=fnt2, fill="#000000", stroke_width=1)
    fnt3 = ImageFont.truetype(f"./fonts/3.ttf", size=200)
    go_txt.text(
        xy=(1400, 50), text=f"{text}", font=fnt3, fill="#000000", stroke_width=1
    )
    fnt4 = ImageFont.truetype(f"./fonts/4.ttf", size=100)
    go_txt.text(
        xy=(200, 400), text=f"{text}", font=fnt4, fill="#000000", stroke_width=1
    )
    fnt5 = ImageFont.truetype(f"./fonts/5.ttf", size=150)
    go_txt.text(
        xy=(800, 400), text=f"{text}", font=fnt5, fill="#000000", stroke_width=1
    )
    fnt6 = ImageFont.truetype(f"./fonts/6.ttf", size=80)
    go_txt.text(xy=(1400, 400), text=f"{text}", font=fnt6, fill="#000000")
    fnt66 = ImageFont.truetype(f"./fonts/66.ttf", size=80)
    go_txt.text(xy=(1430, 440), text=f"k", font=fnt66, fill="#000000")
    txt_blur = txt.filter(ImageFilter.GaussianBlur(radius=1))
    holst = Image.new("RGBA", (1900, 600), (255, 255, 255, 255))
    choose_signature = Image.alpha_composite(holst, txt_blur)
    choose_signature.save(f"./files/it_dl/photo_usr/{user_id}_choose_signature.png")


def create_keyboard(selected_categories: List[str]):
    """
    Функция генерирует клавиатуру с выбором категорий при запросе у пользователя.
    Состояние кнопок "с галочкой" или "без галочки" зависит от переданного массива уже выбранных категорий,
    который перед передачей в функцию запрашивается из State.
    :param selected_categories: List с выбранными категориями
    :return: объект types.InlineKeyboardMarkup
    """
    all_categories = [
        "AM",
        "A1",
        "A2",
        "A",
        "B1",
        "B",
        "C1",
        "C",
        "D1",
        "D",
        "BE",
        "C1E",
        "CE",
        "D1E",
        "DE",
    ]

    kb = types.InlineKeyboardMarkup(row_width=4)
    for i in range(0, len(all_categories), 4):
        row = []
        for j in range(4):
            if i + j < len(all_categories):
                category = all_categories[i + j]
                if category in selected_categories:
                    text = f"{category} ✔️"
                else:
                    text = category
                row.append(
                    types.InlineKeyboardButton(
                        text=text, callback_data=f"cat_{category}"
                    )
                )
        kb.add(*row)
    next_field = types.InlineKeyboardButton(
        text="⏭️Далее", callback_data="cat_nextfield"
    )
    kb.add(back_button_inline, next_field)
    return kb


class FSM(StatesGroup):
    dl_last_name = State()
    dl_first_name = State()
    dl_birth_date = State()
    dl_birth_city = State()
    dl_emission_date = State()
    dl_license_id = State()
    dl_photo = State()
    dl_signature_id = State()
    dl_category_select = State()
    dl_serial_number = State()
    dl_card_serial = State()
    dl_size = State()
    dl_edit_data = State()


back_kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
back_button = types.KeyboardButton(text="Назад ◀️")
back_button_inline = types.InlineKeyboardButton(
    text="Назад ◀️", callback_data="Назад ◀️"
)
back_kb.add(back_button)

data_fields = [
    {"title": "Имя", "key": "dl_first_name"},
    {"title": "Фамилия", "key": "dl_last_name"},
    {"title": "Дата рождения", "key": "dl_birth_date"},
    {"title": "Город рождения", "key": "dl_birth_city"},
    {"title": "Дата выдачи", "key": "dl_emission_date"},
    {"title": "ID лицензии", "key": "dl_license_id"},
    {"title": "Фотография", "key": "dl_photo"},
    {"title": "Подпись", "key": "dl_signature_id"},
    {"title": "Категории", "key": "dl_category_select"},
    {"title": "Серийный номер", "key": "dl_serial_number"},
    {"title": "Серийный номер карточки", "key": "dl_card_serial"},
    {"title": "Размер документа", "key": "dl_size"},
]


async def data_checkup(user_id: int, state: FSMContext):
    try:
        data = await state.get_data()
        checkup_text = "Данные клиента:\n"
        for field in data_fields:
            if field["key"] in data:
                checkup_text += f"✅{field['title']}: {data[field['key']]}\n"
            else:
                checkup_text += f"{field['title']}:\n"
        if "message_to_edit_id" in data:
            await dp.bot.delete_message(
                chat_id=user_id, message_id=data["message_to_edit_id"]
            )
        return (await dp.bot.send_message(chat_id=user_id, text=checkup_text))[
            "message_id"
        ]
    except Exception as _ex:
        logger.exception(f"Ошибка в data_checkup: {_ex}")


async def delete_user_reply(user_id: int, message_id: int):
    try:
        await dp.bot.delete_message(chat_id=user_id, message_id=message_id)
    except Exception as e:
        logger.warning(f"Unable to delete user message: {e}")


@dp.message_handler(Text(equals="DL 🇮🇹"), state="*")
async def dl_first_name_input(message: types.Message, state: FSMContext):
    logger.info(f"Пользователь {message.from_user.id} начал создание документа")
    await sqlite_db.sql_add_country_user(message.from_user.id, "it_dl")
    await state.update_data(type="dl")
    await dp.bot.send_photo(
        chat_id=message.from_user.id,
        photo=InputFile(f"./files/it_dl/source/example.jpg"),
    )
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите имя✏️\n", parse_mode=ParseMode.HTML, reply_markup=back_kb
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM.dl_first_name.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM.dl_first_name
)
async def dl_first_name_validate(message: types.Message, state: FSMContext):
    dl_first_name = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if re.match("^[A-Za-z `]+$", message.text) is not None:
        dl_first_name = message.text
    if dl_first_name is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите имя в правильном формате.\n"
                "<i>Формат ввода: только латинские буквы.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=client_kb.cancelledKeyboard,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл имя")
        await state.update_data(dl_first_name=dl_first_name.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await dl_last_name_input(message, state)


async def dl_last_name_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите фамилию✏️\n", parse_mode=ParseMode.HTML, reply_markup=back_kb
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM.dl_last_name.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM.dl_last_name
)
async def dl_last_name_validate(message: types.Message, state: FSMContext):
    dl_last_name = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if re.match("^[A-Za-z `]+$", message.text) is not None:
        dl_last_name = message.text
    if dl_last_name is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите фамилию в правильном формате.\n"
                "<i>Формат ввода: только латинские буквы.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=client_kb.cancelledKeyboard,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл фамилию")
        await state.update_data(dl_last_name=dl_last_name.upper())
        data["sign"] = dl_last_name.capitalize()
        if len(data["sign"]) > 5:
            data["sign"] = data["sign"][: random.randint(3, 5)]
        await state.update_data(sign=data["sign"])
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await dl_birth_date_input(message, state)


async def dl_birth_date_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите дату рождения✏️\n" "<i>Формат ввода: дд.мм.гггг</i>",
            parse_mode=ParseMode.HTML,
            reply_markup=back_kb,
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM.dl_birth_date.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM.dl_birth_date
)
async def dl_birth_date_validate(message: types.Message, state: FSMContext):
    dl_birth_date = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if re.match(r"^\d{2}\.\d{2}\.\d{4}$", message.text) is not None:
        dl_birth_date = message.text
    if dl_birth_date is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите дату рождения в правильном формате.\n"
                "<i>Формат ввода: дд.мм.гггг</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=back_kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл дату рождения")
        await state.update_data(dl_birth_date=dl_birth_date)
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await dl_birth_city_input(message, state)


async def dl_birth_city_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
    kb.add(types.KeyboardButton(text="Сгенерировать"))
    kb.add(back_button)
    question_message_id = (
        await message.answer(
            "Введите город рождения и его короткое обозначение в скобках✏️\n"
            "<i>Пример: TORINO (TO)</i>\n",
            parse_mode=ParseMode.HTML,
            reply_markup=kb,
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM.dl_birth_city.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM.dl_birth_city
)
async def dl_birth_city_validate(message: types.Message, state: FSMContext):
    dl_birth_city = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()
    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if message.text == "Сгенерировать":
        cities_array = [
            "TORINO (TO)",
            "VERCELLI (VC)",
            "NOVARA (NV)",
            "CUNEO (CN)",
            "ASTI (AT)",
            "ALESSANDRIA (AL)",
            "BIELLA (BI)",
            "VARESE (VA)",
            "COMO (CO)",
            "SONDRIO (SO)",
            "MILANO (MI)",
            "BERGAMO (BG)",
            "BRESCIA (BS)",
            "PAVIA (PV)",
            "CREMONA (CR)",
            "MANTOVA (MN)",
            "LECCO (LC)",
            "LODI (LO)",
            "TRENTO (TN)",
            "VERONA (VR)",
            "VICENZA (VI)",
            "BELLUNO (BL)",
            "TREVISO (TV)",
            "VENEZIA (VE)",
            "PADOVA (PD)",
            "ROVIGO (RO)",
        ]
        dl_birth_city = random.choice(cities_array)
    elif re.match("^[A-Za-z\s]+\s\([A-Za-z]{2}\)$", message.text.upper()) is not None:
        dl_birth_city = message.text
    if dl_birth_city is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
        kb.add(types.KeyboardButton(text="Сгенерировать"))
        question_message_id = (
            await message.answer(
                text="❌Введите город рождения и его короткое название в правильном формате.\n"
                "<i>Формат ввода: Название города и короткое обозначение в скобках, например MILANO (MI)</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл место рождения")
        await state.update_data(dl_birth_city=dl_birth_city.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await dl_emission_date_input(message, state)


async def dl_emission_date_input(message: types.Message, state: FSMContext):
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await message.answer(
            "Введите дату выдачи✏️\n"
            "<i>Формат ввода: дд.мм.гггг</i>\nДата не ранее 19.01.2013!",
            parse_mode=ParseMode.HTML,
            reply_markup=back_kb,
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM.dl_emission_date.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM.dl_emission_date
)
async def dl_emission_date_validate(message: types.Message, state: FSMContext):
    dl_emission_date = None
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()

    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")

    if re.match(r"^\d{2}\.\d{2}\.\d{4}$", message.text) is not None and compare_date(
        "19.01.2013", message.text
    ):
        dl_emission_date = message.text
    if dl_emission_date is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        question_message_id = (
            await message.answer(
                text="❌Введите дату выдачи в правильном формате.\n"
                "<i>Формат ввода: дд.мм.гггг</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=back_kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл дату выдачи")
        await state.update_data(dl_emission_date=dl_emission_date)
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await dl_license_id_input(message, state)


async def dl_license_id_input(message: types.Message, state: FSMContext):
    data = await state.get_data()
    city_code = data["dl_birth_city"].split("(")[-1].split(")")[0]
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
    kb.add(types.KeyboardButton(text="Сгенерировать"))
    kb.add(back_button)
    question_message_id = (
        await message.answer(
            "Введите идентификатор лицензии✏️\n"
            f"<i>Формат ввода: {city_code}YYYYYYYX, Y – цифры, X – латинская заглавная буква.</i>\n",
            parse_mode=ParseMode.HTML,
            reply_markup=kb,
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM.dl_license_id.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM.dl_license_id
)
async def dl_license_id_validate(message: types.Message, state: FSMContext):
    dl_license_id = None
    msg = message.text.upper().strip()
    await delete_user_reply(message.from_user.id, message.message_id)
    data = await state.get_data()
    city_code = data["dl_birth_city"].split("(")[-1].split(")")[0]
    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")
    if message.text == "Сгенерировать":
        dl_license_id = (
            city_code
            + str(random.randint(0, 9999999)).zfill(7)
            + random.choice(string.ascii_uppercase)
        )
    elif re.match(r"[A-Z]{2}\d{7}[A-Z]$", msg) is not None and msg.startswith(
        city_code
    ):
        dl_license_id = msg
    if dl_license_id is None:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
        kb.add(types.KeyboardButton(text="Сгенерировать"))
        question_message_id = (
            await message.answer(
                text=f"❌Введите идентификатор лицензии в правильном формате.\n"
                f"<i>Формат ввода: {city_code}YYYYYYYX, Y – цифры, X – латинская заглавная буква.</i>",
                parse_mode=ParseMode.HTML,
                reply_markup=kb,
            )
        )["message_id"]
        await state.update_data(question_message_id=question_message_id)
    else:
        logger.info(f"Пользователь {message.from_user.id} ввёл идентификатор лицензии")
        await state.update_data(dl_license_id=dl_license_id.upper())
        if "edit_mode" in data:
            return await start_generate_edit(message.from_user.id, state)
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        await dl_photo_input(message, state)


async def dl_photo_input(user_id: int or types.Message, state: FSMContext):
    if type(user_id) == types.Message:
        user_id = user_id.from_user.id
    message_to_edit_id = await data_checkup(user_id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    question_message_id = (
        await dp.bot.send_message(
            chat_id=user_id,
            text="Загрузите фотографию 📷\n"
            " ❗️Требования к фотографии:\n"
            "➖ Фотография должна быть загружена без сжатия, нажимаем скрепку - файл - выбрать фото;\n"
            "➖ На фотографии должно быть видно плечи, глаза открыты;\n"
            "➖ Кадр должен быть в хорошем освещении;\n"
            "➖ Лицо должно быть под прямым углом к камере;\n"
            "Поддерживаемые форматы: JPG, JPEG, PNG ❗️",
            reply_markup=back_kb,
        )
    )["message_id"]
    await FSM.dl_photo.set()
    await state.update_data(question_message_id=question_message_id)


@dp.message_handler(content_types=types.ContentType.DOCUMENT, state=FSM.dl_photo)
async def process_dl_photo(message: types.Message, state: FSMContext):
    data = await state.get_data()
    file_info = await dp.bot.get_file(message.document.file_id)
    await message.delete()
    if file_info.file_path.lower().endswith(".png"):
        photo_path = f"./files/it_dl/photo_usr/{message.from_user.id}.png"
        await dp.bot.download_file(file_info.file_path, photo_path)
        img = Image.open(photo_path).convert("RGBA")
        if img.mode == "RGBA":
            thread = threading.Thread(
                target=process_passport_photo, args=(message.from_user.id,)
            )
            thread.start()
            while thread.is_alive():
                await asyncio.sleep(1)
            processed_photo_path = (
                f"./files/it_dl/photo_usr/{message.from_user.id}_w.png"
            )
            if os.path.exists(processed_photo_path):
                data = await state.get_data()
                question_message_id = data.get("question_message_id")
                try:
                    await dp.bot.delete_message(
                        chat_id=message.chat.id, message_id=question_message_id
                    )
                    message_to_edit_id = await data_checkup(message.from_user.id, state)
                    await state.update_data(message_to_edit_id=message_to_edit_id)
                except:
                    pass
                await state.update_data(dl_photo="Есть")
                logger.info(f"Пользователь {message.from_user.id} отправил фотографию")
                await dl_signature_id_input(message, state)
            else:
                print(1)
                await dp.bot.send_message(
                    chat_id=message.chat.id,
                    text="❌ Фотография не соответствует требованиям.\n"
                    "ИИ не смог обработать вашу фотографию.\n"
                    "Это происходит, если фото не соответствует требованиям, "
                    "проверьте что расширение файла PNG, "
                    "либо загрузите другое изображение.",
                )
        else:
            print(2)
            await dp.bot.send_message(
                chat_id=message.chat.id,
                text="❌ Фотография не соответствует требованиям.\n"
                "ИИ не смог обработать вашу фотографию.\n"
                "Это происходит, если фото не соответствует требованиям, "
                "проверьте что расширение файла PNG, "
                "либо загрузите другое изображение.",
            )
    elif file_info.file_path.lower().endswith(".jpg"):
        photo_path = f"./files/it_dl/photo_usr/{message.from_user.id}.png"
        await dp.bot.download_file(
            file_info.file_path, f"./files/it_dl/photo_usr/{message.from_user.id}.jpg"
        )
        # AI work

        from rembg import remove

        out = remove(
            Image.open(f"./files/it_dl/photo_usr/{message.from_user.id}.jpg"),
            True,
            240,
            240,
        )
        out = out.convert("RGBA")
        out.save(f"./files/it_dl/photo_usr/{message.from_user.id}.png")
        img = Image.open(photo_path).convert("RGBA")
        if img.mode == "RGBA":
            thread = threading.Thread(
                target=process_passport_photo, args=(message.from_user.id,)
            )
            thread.start()
            while thread.is_alive():
                await asyncio.sleep(1)
            processed_photo_path = (
                f"./files/it_dl/photo_usr/{message.from_user.id}_w.png"
            )
            if os.path.exists(processed_photo_path):
                data = await state.get_data()
                question_message_id = data.get("question_message_id")
                try:
                    await dp.bot.delete_message(
                        chat_id=message.chat.id, message_id=question_message_id
                    )
                    message_to_edit_id = await data_checkup(message.from_user.id, state)
                    await state.update_data(message_to_edit_id=message_to_edit_id)
                except:
                    pass
                await state.update_data(dl_photo="Есть")
                logger.info(f"Пользователь {message.from_user.id} отправил фотографию")
                await dl_signature_id_input(message, state)

            else:
                print(1)
                await dp.bot.send_message(
                    chat_id=message.chat.id,
                    text="❌ Фотография не соответствует требованиям.\n"
                    "ИИ не смог обработать вашу фотографию.\n"
                    "Это происходит, если фото не соответствует требованиям, "
                    "проверьте что расширение файла PNG, "
                    "либо загрузите другое изображение.",
                )
        else:
            print(2)
            await dp.bot.send_message(
                chat_id=message.chat.id,
                text="❌ Фотография не соответствует требованиям.\n"
                "ИИ не смог обработать вашу фотографию.\n"
                "Это происходит, если фото не соответствует требованиям, "
                "проверьте что расширение файла PNG, "
                "либо загрузите другое изображение.",
            )

    elif file_info.file_path.lower().endswith(".jpeg"):
        photo_path = f"./files/it_dl/photo_usr/{message.from_user.id}.png"
        await dp.bot.download_file(
            file_info.file_path, f"./files/it_dl/photo_usr/{message.from_user.id}.jpeg"
        )
        # AI work

        from rembg import remove

        out = remove(
            Image.open(f"./files/it_dl/photo_usr/{message.from_user.id}.jpeg"),
            True,
            240,
            240,
        )
        out = out.convert("RGBA")
        out.save(f"./files/it_dl/photo_usr/{message.from_user.id}.png")
        img = Image.open(photo_path).convert("RGBA")
        if img.mode == "RGBA":
            thread = threading.Thread(
                target=process_passport_photo, args=(message.from_user.id,)
            )
            thread.start()
            while thread.is_alive():
                await asyncio.sleep(1)
            processed_photo_path = (
                f"./files/it_dl/photo_usr/{message.from_user.id}_w.png"
            )
            if os.path.exists(processed_photo_path):
                data = await state.get_data()
                question_message_id = data.get("question_message_id")
                try:
                    await dp.bot.delete_message(
                        chat_id=message.chat.id, message_id=question_message_id
                    )
                    message_to_edit_id = await data_checkup(message.from_user.id, state)
                    await state.update_data(message_to_edit_id=message_to_edit_id)
                except:
                    pass
                await state.update_data(dl_photo="Есть")
                logger.info(f"Пользователь {message.from_user.id} отправил фотографию")
                await dl_signature_id_input(message, state)
            else:
                print(1)
                await dp.bot.send_message(
                    chat_id=message.chat.id,
                    text="❌ Фотография не соответствует требованиям.\n"
                    "ИИ не смог обработать вашу фотографию.\n"
                    "Это происходит, если фото не соответствует требованиям, "
                    "проверьте что расширение файла PNG, "
                    "либо загрузите другое изображение.",
                )
        else:
            print(2)
            await dp.bot.send_message(
                chat_id=message.chat.id,
                text="❌ Фотография не соответствует требованиям.\n"
                "ИИ не смог обработать вашу фотографию.\n"
                "Это происходит, если фото не соответствует требованиям, "
                "проверьте что расширение файла PNG, "
                "либо загрузите другое изображение.",
            )
    else:
        print(3)
        await dp.bot.send_message(
            chat_id=message.chat.id,
            text="❌ Фотография не соответствует требованиям.\n"
            "ИИ не смог обработать вашу фотографию.\n"
            "Это происходит, если фото не соответствует требованиям, "
            "проверьте что расширение файла PNG, "
            "либо загрузите другое изображение.",
        )


@dp.message_handler(lambda message: "Назад ◀️" not in message.text, state=FSM.dl_photo)
async def process_dl_photo_nontype(message: types.Message, state: FSMContext):
    await message.delete()
    await message.answer(
        "Вы должны отправить фотографию без фона в формате PNG и без сжатия."
    )


@dp.message_handler(content_types=types.ContentType.PHOTO, state=FSM.dl_photo)
async def process_dl_photo_nontype_photo(message: types.Message, state: FSMContext):
    await message.delete()
    await message.answer(
        "Вы должны отправить фотографию без фона в формате PNG и без сжатия."
    )


async def dl_signature_id_input(user_id, state: FSMContext):
    if type(user_id) == types.Message:
        user_id = user_id.from_user.id
    message_to_edit_id = await data_checkup(user_id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    data = await state.get_data()

    process_generate_signature(user_id=user_id, data=data["sign"])

    kb = types.InlineKeyboardMarkup()
    kb.add(
        types.InlineKeyboardButton(text="1", callback_data="sig_1"),
        types.InlineKeyboardButton(text="2", callback_data="sig_2"),
        types.InlineKeyboardButton(text="3", callback_data="sig_3"),
    )
    kb.add(
        types.InlineKeyboardButton(text="4", callback_data="sig_4"),
        types.InlineKeyboardButton(text="5", callback_data="sig_5"),
        types.InlineKeyboardButton(text="6", callback_data="sig_6"),
    )
    kb.add(
        types.InlineKeyboardButton(text="Не ставить", callback_data="sig_0"),
        back_button_inline,
    )

    photo_path = f"./files/it_dl/photo_usr/{user_id}_choose_signature.png"  # Замените на путь к вашей картинке
    with open(photo_path, "rb") as photo:
        question_message_id = (
            await dp.bot.send_photo(
                chat_id=user_id,
                photo=photo,
                caption="Выберите тип подписи:",
                reply_markup=kb,
            )
        )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM.dl_signature_id.set()


@dp.callback_query_handler(
    lambda c: c.data.startswith("sig_") and "Назад ◀️" not in c.data,
    state=FSM.dl_signature_id,
)
async def dl_signature_id_validate(
    callback_query: types.CallbackQuery, state: FSMContext
):
    await dp.bot.answer_callback_query(callback_query.id)
    signature_id = int(callback_query.data.split("_")[1])
    await state.update_data(dl_signature_id=signature_id)
    logger.info(f"Пользователь {callback_query.from_user.id} выбрал подпись")
    data = await state.get_data()
    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=callback_query.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")
    if "edit_mode" in data:
        return await start_generate_edit(callback_query.from_user.id, state)
    message_to_edit_id = await data_checkup(callback_query.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    await dl_category_select_input(callback_query.from_user.id, state)


async def dl_category_select_input(user_id: int or types.Message, state: FSMContext):
    if type(user_id) == types.Message:
        user_id = user_id.from_user.id
    message_to_edit_id = await data_checkup(user_id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    data = await state.get_data()
    selected_categories = data.get("dl_category_select", [])
    await dp.bot.send_message(
        chat_id=user_id,
        text="Выберите категории:",
        reply_markup=create_keyboard(selected_categories),
    )
    await FSM.dl_category_select.set()


@dp.callback_query_handler(
    lambda c: c.data.startswith("cat_"), state=FSM.dl_category_select
)
async def process_category_select(
    callback_query: types.CallbackQuery, state: FSMContext
):
    category = callback_query.data.split("_")[1]
    data = await state.get_data()
    if category == "nextfield":
        await dp.bot.delete_message(
            chat_id=callback_query.from_user.id,
            message_id=callback_query.message.message_id,
        )
        if "edit_mode" in data:
            return await start_generate_edit(callback_query.from_user.id, state)
        return await dl_serial_number_input(callback_query.from_user.id, state)
    await dp.bot.answer_callback_query(callback_query.id)
    selected_categories = data.get("dl_category_select", [])
    if category in selected_categories:
        selected_categories.remove(category)
    else:
        selected_categories.append(category)
    await state.update_data(dl_category_select=selected_categories)
    await dp.bot.edit_message_reply_markup(
        chat_id=callback_query.from_user.id,
        message_id=callback_query.message.message_id,
        reply_markup=create_keyboard(selected_categories),
    )


async def dl_serial_number_input(user_id: int or types.Message, state: FSMContext):
    if type(user_id) == types.Message:
        user_id = user_id.from_user.id
    message_to_edit_id = await data_checkup(user_id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    data = await state.get_data()
    city_code = data["dl_birth_city"].split("(")[-1].split(")")[0]

    kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
    kb.add(types.KeyboardButton(text="Сгенерировать"))
    kb.add(back_button)

    question_message_id = (
        await dp.bot.send_message(
            chat_id=user_id,
            text=f"Введите серийный номер документа. Он должен начинаться с {city_code} и иметь формат {city_code}XXXXXXX I",
            reply_markup=kb,
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)
    await FSM.dl_serial_number.set()


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM.dl_serial_number
)
async def dl_serial_number_validate(message: types.Message, state: FSMContext):
    text = message.text.strip()
    await message.delete()
    if text.lower() == "сгенерировать":
        data = await state.get_data()
        city_code = data["dl_birth_city"].split("(")[-1].split(")")[0]
        random_digits = "".join(str(random.randint(0, 9)) for _ in range(7))
        serial_number = f"{city_code}{random_digits} I"
    else:
        data = await state.get_data()
        city_code = data["dl_birth_city"].split("(")[-1].split(")")[0]
        pattern = r"^[A-Z]{2}\d{7} I$"
        if re.match(pattern, text) and text.startswith(city_code):
            serial_number = text
        else:
            return await message.answer(
                f"❌ Неверный формат серийного номера. Пожалуйста, попробуйте еще раз. Формат должен быть: {city_code}XXXXXXX I"
            )

    question_message_id = data.get("question_message_id")
    try:
        await dp.bot.delete_message(
            chat_id=message.chat.id, message_id=question_message_id
        )
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
    except:
        pass
    await state.update_data(dl_serial_number=serial_number)
    if "edit_mode" in data:
        return await start_generate_edit(message.from_user.id, state)
    await dl_card_serial_input(message, state)


async def dl_card_serial_input(user_id, state: FSMContext):
    if type(user_id) == types.Message:
        user_id = user_id.from_user.id
    message_to_edit_id = await data_checkup(user_id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    await FSM.dl_card_serial.set()
    kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
    kb.add(types.KeyboardButton(text="Сгенерировать"))
    kb.add(back_button)
    question_message_id = (
        await dp.bot.send_message(
            user_id,
            "Введите серийный номер карточки (формат: AF XXXXXXX или AE XXXXXXX) или нажмите 'Сгенерировать'",
            reply_markup=kb,
        )
    )["message_id"]
    await state.update_data(question_message_id=question_message_id)


@dp.message_handler(
    lambda message: "Назад ◀️" not in message.text, state=FSM.dl_card_serial
)
async def dl_card_serial_validate(message: types.Message, state: FSMContext):
    text = message.text.strip()
    await message.delete()
    if text.lower() == "сгенерировать":
        random_digits = "".join(str(random.randint(0, 9)) for _ in range(7))
        card_serial = (
            f"AF {random_digits}"
            if random.randint(0, 1) == 0
            else f"AE {random_digits}"
        )
    else:
        pattern = "^(AF|AE) \d{7}$"
        if re.match(pattern, text):
            card_serial = text
        else:
            return await message.answer(
                "❌ Неверный формат серийного номера карточки. Пожалуйста, попробуйте еще раз. Формат должен быть: AF XXXXXXX или AE XXXXXXX"
            )
    data = await state.get_data()
    question_message_id = (await state.get_data()).get("question_message_id")
    try:
        await dp.bot.delete_message(
            chat_id=message.chat.id, message_id=question_message_id
        )
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
    except:
        pass
    await state.update_data(dl_card_serial=card_serial)
    if "edit_mode" in data:
        return await start_generate_edit(message.from_user.id, state)
    await dl_size_input(message, state)


async def dl_size_input(user_id: int or types.Message, state: FSMContext):
    if type(user_id) == types.Message:
        user_id = user_id.from_user.id
    message_to_edit_id = await data_checkup(user_id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    kb = types.ReplyKeyboardMarkup(resize_keyboard=True, one_time_keyboard=True)
    kb.add(types.KeyboardButton(text="📃На листе А4"))
    kb.add(types.KeyboardButton(text="🪪В размер документа"))
    kb.add(back_button)
    question_message_id = (
        await dp.bot.send_message(
            chat_id=user_id, text="Выберите размер документа", reply_markup=kb
        )
    )["message_id"]
    await FSM.dl_size.set()
    await state.update_data(question_message_id=question_message_id)


@dp.message_handler(lambda message: "Назад ◀️" not in message.text, state=FSM.dl_size)
async def dl_select_size_validate(message: types.Message, state: FSMContext):
    if message.text == "📃На листе А4":
        size_type = "A4"
    elif message.text == "🪪В размер документа":
        size_type = "DOC"
    else:
        message_to_edit_id = await data_checkup(message.from_user.id, state)
        await state.update_data(message_to_edit_id=message_to_edit_id)
        kb = types.ReplyKeyboardMarkup(resize_keyboard=True)
        kb.add(types.KeyboardButton(text="📃На листе А4"))
        kb.add(types.KeyboardButton(text="🪪В размер документа"))
        kb.add(back_button)
        question_message_id = (
            await message.answer(
                text="❌Выберите тип через клавиатуру.\n",
                parse_mode=ParseMode.HTML,
                reply_markup=kb,
            )
        )["message_id"]
        return await state.update_data(question_message_id=question_message_id)
    await message.delete()
    await state.update_data(size=size_type)
    logger.info(f"Пользователь {message.from_user.id} выбрал размер документа")
    data = await state.get_data()
    if "question_message_id" in data:
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=data["question_message_id"]
        )
    else:
        logger.warning("question_message_id not found in state data")
    if "edit_mode" in data:
        return await start_generate_edit(message.from_user.id, state)
    message_to_edit_id = await data_checkup(message.from_user.id, state)
    await state.update_data(message_to_edit_id=message_to_edit_id)
    await start_generate(message, state)


async def start_generate(message: types.Message, state: FSMContext):
    message_to_delete = await message.answer("Ожидайте генерируется...")
    await dp.bot.send_chat_action(message.chat.id, "upload_photo")
    await state.update_data(progress_message_id=message_to_delete["message_id"])
    data = await state.get_data()
    await dp.bot.delete_message(
        chat_id=message.from_user.id, message_id=data["message_to_edit_id"]
    )
    logger.info(f"Начинаем генерацию документа для пользователя {message.from_user.id}")
    result_queue = queue.Queue()
    id_thread = threading.Thread(
        target=generate_dl, args=(data, message.from_user.id, result_queue)
    )
    id_thread.start()
    await dp.bot.send_chat_action(message.chat.id, "upload_photo")
    id_thread.join()
    result = result_queue.get()
    if "Error" in result:
        logger.error(
            f"Ошибка при генерации документа для пользователя {message.from_user.id}:\n{result}"
        )
        await message.answer("❌Ошибка при генерации изображения")
        await dp.bot.send_message(chat_id=message.from_user.id, text=result)
    else:
        logger.info(
            f"Успешно завершили генерацию документа для пользователя {message.from_user.id}"
        )
        await dp.bot.delete_message(
            chat_id=message.from_user.id, message_id=message_to_delete["message_id"]
        )
        with open(result + "_wm.png", "rb") as photo:
            await dp.bot.send_document(chat_id=message.from_user.id, document=photo)
            await message.answer(
                "Завершите покупку документа.", reply_markup=client_kb.kb_it_finish_dl
            )


async def start_generate_edit(user_id: int, state: FSMContext):
    message_to_delete = await dp.bot.send_message(
        chat_id=user_id, text="Ожидайте генерируется..."
    )
    await dp.bot.send_chat_action(user_id, "upload_photo")
    await state.update_data(progress_message_id=message_to_delete["message_id"])
    data = await state.get_data()
    result_queue = queue.Queue()
    id_thread = threading.Thread(target=generate_dl, args=(data, user_id, result_queue))
    id_thread.start()
    await dp.bot.send_chat_action(user_id, "upload_photo")
    id_thread.join()
    result = result_queue.get()
    if "Error" in result:
        await dp.bot.send_message(
            chat_id=user_id, text="❌Ошибка при генерации изображения"
        )
    else:
        await dp.bot.delete_message(
            chat_id=user_id, message_id=message_to_delete["message_id"]
        )
        with open(result + "_wm.png", "rb") as photo:
            await dp.bot.send_document(chat_id=user_id, document=photo)
            await dp.bot.send_message(
                chat_id=user_id,
                text="Завершите покупку документа.",
                reply_markup=client_kb.kb_it_finish_dl,
            )


@dp.callback_query_handler(text="it_change_dl", state="*")
async def it_change_dl(call: types.CallbackQuery, state: FSMContext):
    kb = types.ReplyKeyboardMarkup(one_time_keyboard=True)
    await call.message.delete()
    for field in data_fields:
        kb.add(types.KeyboardButton(text=f'{field["title"]}'))
    edit_message_to_delete_id = (
        await call.message.answer(
            text="Выберите параметр, который хотите изменить", reply_markup=kb
        )
    )["message_id"]
    await state.update_data(edit_message_to_delete_id=edit_message_to_delete_id)
    await FSM.dl_edit_data.set()


@dp.message_handler(state=FSM.dl_edit_data)
async def edit_field(message: types.Message, state: FSMContext):
    data = await state.get_data()
    await dp.bot.delete_message(
        chat_id=message.from_user.id, message_id=int(data["edit_message_to_delete_id"])
    )
    await message.delete()
    for field in data_fields:
        if field["title"] == message.text:
            key = field["key"]
    await state.update_data(edit_mode=1)
    func_name = key
    func_name_with_input = func_name + "_input"

    # Получаем ссылку на функцию с использованием имени и вызываем ее
    function_to_call = globals()[func_name_with_input]
    await function_to_call(message, state)


state_input_functions = {
    "FSM:dl_last_name": create_italy,
    "FSM:dl_first_name": dl_last_name_input,
    "FSM:dl_birth_date": dl_first_name_input,
    "FSM:dl_birth_city": dl_birth_date_input,
    "FSM:dl_emission_date": dl_birth_city_input,
    "FSM:dl_license_id": dl_emission_date_input,
    "FSM:dl_photo": dl_license_id_input,
    "FSM:dl_signature_id": dl_photo_input,
    "FSM:dl_category_select": dl_signature_id_input,
    "FSM:dl_serial_number": dl_category_select_input,
    "FSM:dl_card_serial": dl_serial_number_input,
    "FSM:dl_size": dl_card_serial_input,
}


@dp.message_handler(lambda message: message.text == "Назад ◀️", state=FSM)
async def go_back(message: types.Message, state: FSMContext):
    current_state = str(await state.get_state())
    logger.info(
        f"Пользователь {message.from_user.username} вернулся назад с поля {current_state}"
    )
    if current_state in state_input_functions:
        input_function = state_input_functions[current_state]
        await input_function(
            message, state
        )  # вызов функции ввода для предыдущего состояния
    else:
        await message.answer("Unknown state.")


@dp.callback_query_handler(lambda call: call.data == "Назад ◀️", state="*")
async def go_back(call: types.CallbackQuery, state: FSMContext):
    await call.message.delete()
    current_state = str(await state.get_state())
    logger.info(
        f"Пользователь {call.from_user.username} вернулся назад с поля {current_state}"
    )
    if current_state in state_input_functions:
        input_function = state_input_functions[current_state]
        await input_function(
            call.message, state
        )  # вызов функции ввода для предыдущего состояния
    else:
        await call.message.answer("Unknown state.")


@dp.callback_query_handler(text="it_pay_dl", state="*")
async def command_pay_it_dl(callback: types.CallbackQuery, state: FSMContext):
    await state.finish()
    await state.update_data(pay_country="it_dl")
    await client.command_promo_quest(callback, state)


def generate_dl(data, user_id, result_queue):
    try:
        import datetime
        import random

        import codicefiscale as codicefiscale
        from mrz.generator.td1 import TD1CodeGenerator
        from PIL import Image, ImageDraw, ImageEnhance, ImageFilter, ImageFont

        categories_chords = {
            "AM": [(878, 120), (1143, 120)],
            "A1": [(878, 180), (1143, 180)],
            "A2": [(878, 240), (1143, 240)],
            "A": [(878, 300), (1143, 300)],
            "B1": [(878, 358), (1143, 358)],
            "B": [(878, 417), (1143, 417)],
            "C1": [(878, 477), (1143, 477)],
            "C": [(878, 537), (1143, 537)],
            "D1": [(878, 594), (1143, 594)],
            "D": [(878, 652), (1143, 652)],
            "BE": [(878, 714), (1143, 714)],
            "C1E": [(878, 772), (1143, 772)],
            "CE": [(878, 829), (1143, 829)],
            "D1E": [(878, 890), (1143, 890)],
            "DE": [(878, 950), (1143, 950)],
        }

        # Загрузка исходного изображения
        image = Image.open(f"./files/it_dl/source/front.png").convert("RGBA")
        # Создание объекта ImageDraw для рисования на изображении
        draw = ImageDraw.Draw(image)

        # Извлечение переменных из данных
        first_name = data.get("dl_first_name", "")  # 1
        last_name = data.get("dl_last_name", "")  # 2
        birth_date_str = data.get("dl_birth_date", "")  # 3.1
        birth_date = datetime.datetime.strptime(birth_date_str, "%d.%m.%Y")

        # Получение текущей даты
        now = datetime.datetime.now()

        # Вычисление возраста в годах
        age = relativedelta(now, birth_date).years

        # Генерация переменной c4
        birth_place = data.get("dl_birth_city", "")
        # Извлечение букв в скобках
        match = re.search(r"\((.*?)\)", birth_place)
        letters_in_brackets = match.group(1)
        if age >= 35:
            c4 = f"MIT-UCO"
        else:
            c4 = f"MCTC-{letters_in_brackets}"

        categories_emission_date_str = data.get("dl_emission_date", "")
        categories_emission_date = datetime.datetime.strptime(
            categories_emission_date_str, "%d.%m.%Y"
        )
        categories_emission_date = categories_emission_date + relativedelta(
            days=random.randint(14, 30)
        )
        a4_date = data.get("dl_emission_date", "")  # 4a
        a4_date_str = datetime.datetime.strptime(a4_date, "%d.%m.%Y").strftime(
            "%d/%m/%Y"
        )
        a4_date_datetime = datetime.datetime.strptime(a4_date, "%d.%m.%Y")
        if age < 50:
            categories_expire_date = a4_date_datetime + relativedelta(years=10)
            b4_date_datetime = a4_date_datetime + relativedelta(years=10)
        elif age < 70:
            categories_expire_date = a4_date_datetime + relativedelta(years=5)
            b4_date_datetime = a4_date_datetime + relativedelta(years=5)
        else:
            categories_expire_date = a4_date_datetime + relativedelta(years=3)
            b4_date_datetime = a4_date_datetime + relativedelta(years=3)
        b4_date_datetime = b4_date_datetime - datetime.timedelta(days=1)
        categories_expire_date = categories_expire_date - relativedelta(days=1)
        categories_expire_date_str = categories_expire_date.strftime("%d.%m.%Y")

        birth_place = data.get("dl_birth_city", "")  # 3.2
        string_5 = data.get("dl_license_id", "")  # 5
        card_number = data.get("dl_card_serial", "")
        dl_number = data.get("dl_serial_number", "")
        categories_array = data.get("dl_category_select", [])
        signature_id = data.get("dl_signature_id", 4)  # 7
        string_9 = ""  # 9
        for category in categories_array:
            string_9 += f"{category}  "
        b4_date_str = b4_date_datetime.strftime("%d/%m/%Y")
        birth_date_str = datetime.datetime.strptime(
            birth_date_str, "%d.%m.%Y"
        ).strftime("%d/%m/%y")

        categories_emission_date_str = categories_emission_date.strftime("%d/%m/%y")
        categories_expire_date_str = datetime.datetime.strptime(
            categories_expire_date_str, "%d.%m.%Y"
        ).strftime("%d/%m/%y")

        text_image = Image.new("RGBA", image.size, (255, 255, 255, 0))
        # Рендер имени
        text_draw = ImageDraw.Draw(text_image)
        font = ImageFont.truetype(f"./fonts/Arial.ttf", 55)
        x, y = 700, 195
        text_draw.text(
            xy=(x, y),
            text=first_name,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=0,
            anchor="lm",
        )

        # Рендер фамилии
        x, y = 700, 280
        text_draw.text(
            xy=(x, y),
            text=last_name,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=0,
            anchor="lm",
        )

        # Рендер даты и места рождения
        x, y = 700, 366
        text_draw.text(
            xy=(x, y),
            text=birth_date_str + "    " + birth_place,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=0,
            anchor="lm",
        )

        # Рендер 4a
        font = ImageFont.truetype(f"./fonts/Arial.ttf", 60)
        x, y = 700, 454
        text_draw.text(
            xy=(x, y),
            text=a4_date_str,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=1,
            anchor="lm",
        )

        # Рендер 4b
        x, y = 700, 538
        text_draw.text(
            xy=(x, y),
            text=b4_date_str,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=1,
            anchor="lm",
        )

        # Рендер c4
        font = ImageFont.truetype(f"./fonts/Arial.ttf", 55)
        x, y = 1177, 456
        text_draw.text(
            xy=(x, y),
            text=c4,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=0,
            anchor="lm",
        )

        # Рендер строки №5
        x, y = 697, 631
        text_draw.text(
            xy=(x, y),
            text=string_5,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=0,
            anchor="lm",
        )

        # Рендер строки 9
        x, y = 147, 999
        text_draw.text(
            xy=(x, y),
            text=string_9,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=0,
            anchor="lm",
        )

        text_image = text_image.filter(ImageFilter.GaussianBlur(radius=0.9))
        image = Image.alpha_composite(image, text_image)

        # Рендер подписи
        sign = data["sign"].capitalize()
        if signature_id != 0:
            fonts_dict = {
                1: ImageFont.truetype(f"./fonts/1.ttf", size=300),
                2: ImageFont.truetype(f"./fonts/2.ttf", size=190),
                3: ImageFont.truetype(f"./fonts/3.ttf", size=250),
                4: ImageFont.truetype(f"./fonts/4.ttf", size=170),
                5: ImageFont.truetype(f"./fonts/5.ttf", size=220),
                6: ImageFont.truetype(f"./fonts/6.ttf", size=160),
                7: ImageFont.truetype(f"./fonts/66.ttf", size=90),
            }
            if signature_id == 6:
                font = fonts_dict[5]
                x, y = 830, 778
                txt = Image.new("RGBA", image.size, (255, 255, 255, 0))
                go_txt = ImageDraw.Draw(txt)
                go_txt.text(
                    xy=(x, y),
                    text=sign,
                    font=font,
                    fill=(29, 29, 29, 255),
                    stroke_width=0,
                    anchor="lm",
                )
                go_txt.text(
                    xy=(x + 50, y - 15),
                    text=f"k",
                    font=fonts_dict[7],
                    fill=(29, 29, 29, 255),
                )
                txt_blur = txt.filter(ImageFilter.GaussianBlur(radius=0.5))
                # Создайте новый слой подписи с размером, соответствующим изображению документа
                signature_layer = Image.new("RGBA", image.size, (255, 255, 255, 0))

                # Вставьте изображение подписи на слой подписи
                signature_position = (0, 0)  # координаты x, y для вставки подписи
                signature_layer.paste(txt_blur, signature_position)

                # Объедините изображение документа и слой подписи
                image = Image.alpha_composite(image, signature_layer)
            else:
                font = fonts_dict[signature_id]
                x, y = 830, 778
                txt = Image.new("RGBA", image.size, (255, 255, 255, 0))
                go_txt = ImageDraw.Draw(txt)
                go_txt.text(
                    xy=(x, y),
                    text=sign,
                    font=font,
                    fill=(29, 29, 29, 255),
                    stroke_width=0,
                    anchor="lm",
                )
                txt_blur = txt.filter(ImageFilter.GaussianBlur(radius=0.9))
                # Создайте новый слой подписи с размером, соответствующим изображению документа
                signature_layer = Image.new("RGBA", image.size, (255, 255, 255, 0))

                # Вставьте изображение подписи на слой подписи
                signature_position = (0, 30)  # координаты x, y для вставки подписи
                signature_layer.paste(txt_blur, signature_position)

                # Объедините изображение документа и слой подписи
                image = Image.alpha_composite(image, signature_layer)

        # process_passport_photo(Admins[0])
        # Рендер фотографии
        def multiply_images(image1, image2):
            if image1.size != image2.size:
                print("Изображения должны быть одного размера")
                return None

            result_image = Image.new("RGBA", image1.size, (0, 0, 0, 0))
            for x in range(image1.width):
                for y in range(image1.height):
                    pixel1 = image1.getpixel((x, y))
                    pixel2 = image2.getpixel((x, y))
                    new_pixel = tuple(int(a * b / 255) for a, b in zip(pixel1, pixel2))
                    result_image.putpixel((x, y), new_pixel)

            return result_image

        overlay_image = Image.open(f"./files/it_dl/photo_usr/{user_id}_w.png").convert(
            "RGBA"
        )

        overlay_position = (
            73,
            343,
        )  # координаты x, y для вставки обрезанного изображения
        overlay_layer = Image.new("RGBA", image.size, (255, 255, 255, 0))
        overlay_layer.paste(overlay_image, overlay_position, overlay_image)

        # Используйте функцию multiply_images() для наложения overlay_image на image
        overlay_layer = multiply_images(image, overlay_layer)
        overlay_layer = overlay_layer.filter(ImageFilter.GaussianBlur(radius=0.9))
        # Объедините исходное изображение и новый слой с помощью `Image.alpha_composite()`
        image = Image.alpha_composite(image, overlay_layer)
        front = image  # промежуточное сохранение первой стороны. потом будем наклыдывать две стороны на пустой фон

        # Загрузка исходного изображения второй стороны
        image = Image.open(f"./files/it_dl/source/back.png").convert("RGBA")

        # Создаем новое изображение с размерами исходного изображения для текста
        text_layer = Image.new("RGBA", image.size, (255, 255, 255, 0))
        draw_on_text_layer = ImageDraw.Draw(text_layer)

        # Устанавливаем новый размер шрифта
        font = ImageFont.truetype(f"./fonts/Arial.ttf", 43)

        # Итерируем по массиву категорий
        for category in categories_array:
            # Получаем координаты для текущей категории
            coords = categories_chords[category]
            # Добавляем дату начала на слой текста
            draw_on_text_layer.text(
                xy=coords[0],
                text=categories_emission_date_str,
                font=font,
                fill=(0, 0, 0, 255),
                stroke_width=0,
                anchor="lm",
            )
            # Добавляем дату окончания на слой текста
            draw_on_text_layer.text(
                xy=coords[1],
                text=categories_expire_date_str,
                font=font,
                fill=(0, 0, 0, 255),
                stroke_width=0,
                anchor="lm",
            )

        # Применяем размытие к слою с текстом
        text_layer = text_layer.filter(ImageFilter.GaussianBlur(radius=0.2))

        # Накладываем текст с эффектом размытия на исходное изображение
        image.paste(text_layer, (0, 0), text_layer)

        """
        Нанесение CARD NUMBER
        """
        # Создание объекта ImageDraw для рисования на изображении
        draw = ImageDraw.Draw(image)
        font = ImageFont.truetype(f"./fonts/Helvetica_Condenced-Normal.ttf", 75)
        spacing = 40  # Устанавливаем желаемый межсимвольный интервал
        x, y = 1427, 1175  # Исходные координаты

        # Создаем новое изображение с размерами исходного изображения
        text_layer = Image.new("RGBA", image.size, (255, 255, 255, 0))
        draw_on_text_layer = ImageDraw.Draw(text_layer)

        for i, char in enumerate(card_number):
            draw_on_text_layer.text(
                xy=(x + i * spacing, y),
                text=char,
                font=font,
                fill=(0, 0, 0, 255),
                stroke_width=2,
                anchor="lm",
            )

        # Применяем размытие к слою с текстом
        text_layer = text_layer.filter(ImageFilter.GaussianBlur(radius=1.1))

        # Накладываем текст с эффектом размытия на исходное изображение
        image.paste(text_layer, (0, 0), text_layer)

        # Добавляем DL Number
        font = ImageFont.truetype(f"./fonts/Arial.ttf", 55)
        draw.text(
            xy=(372, 1145),
            text=dl_number,
            font=font,
            fill=(0, 0, 0, 255),
            stroke_width=0,
            anchor="lm",
        )

        back = image

        # Установка отступа
        gap = 50

        image_name = f"./files/it_dl/photo_usr/{user_id}_completed"
        if data["size"] == "DOC":
            # Создание нового изображения с нужными размерами
            image = Image.new(
                "RGBA",
                (front.width + back.width + gap, front.height),
                (255, 255, 255, 0),
            )

            # Вставка изображений в новое изображение
            image.paste(front, (0, 0))
            image.paste(back, (front.width + gap, 0))
            # Сохранение полученного результата
            image.save(image_name + ".png")

        elif data["size"] == "A4":
            # Создание нового изображения с нужными размерами
            image = Image.new(
                "RGBA",
                (front.width + back.width + gap, front.height),
                (255, 255, 255, 255),
            )

            # Вставка изображений в новое изображение
            image.alpha_composite(front, (0, 0))
            image.alpha_composite(back, (front.width + gap, 0))
            # Задаем размеры A4 в миллиметрах
            width_mm = 210
            height_mm = 297

            # Задаем разрешение изображения в DPI
            resolution_dpi = 600

            # Конвертируем размеры A4 в пиксели
            width_px = int(width_mm * resolution_dpi / 25.4)
            height_px = int(height_mm * resolution_dpi / 25.4)

            # Создаем новое изображение размером A4 с белым фоном
            image_a4 = Image.new("RGBA", (width_px, height_px), "white")
            new_width = 4100  # Задаем новую ширину в пикселях
            new_height = int(
                image.height * new_width / image.width
            )  # Вычисляем новую высоту, сохраняя соотношение сторон
            image = image.resize((new_width, new_height), Image.ANTIALIAS)
            image_a4.paste(image, (50, 50))

            # Сохраняем изображение в файл
            image_a4.save(image_name + ".png", "PNG")
        else:
            raise ValueError

        paste_watermark(
            f"./files/it_dl/photo_usr/{user_id}_completed.png",
            f"./files/it_dl/photo_usr/{user_id}_completed_wm.png",
        )

        result_queue.put(image_name)
    except KeyError as _ex:
        logger.error(_ex)
        result_queue.put(f"Error:Ошибка в предоставленных данных:\n")
    except ValueError as _ex:
        logger.error(_ex)
        result_queue.put(f"Error:Ошибка при сохранении результата:\n")
    except Exception as _ex:
        logger.error(_ex)
        result_queue.put(f"Error:Ошибка при генерации:\n")
